﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Reflection;
using System.Net;
using System.IO;
using GanagroLiteData;
using Csla.Validation;
using System.IO.Compression;
using System.ComponentModel;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using GeneralUtilities;

namespace GanagroLiteBO
{
    public enum TiposOperacion
    {
        Pesaje_Carne = 1,
        compra = 6,
        venta = 7,
        Muerte = 9,
        Otras = 31
    }

    public enum TiposGananciaDiaria
    {
        Buena = 1,
        Regular = 2,
        Perdida = 3,
        Erronea = 4
    }

    public enum Estados
    {
        Ternero = 1,
        Levante = 2,
        Parida = 3,
        escotera = 4,
        toro = 5,
        NovillaDeVientre = 9,
        ceba = 10
    }

    public enum ModoForma
    {
        inicializando,
        cargando,
        cargada,
        editar,
        ver
    }

    public static class PredicateBuilder
    {
      public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
      public static Expression<Func<T, bool>> False<T> () { return f => false; }
 
      public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                          Expression<Func<T, bool>> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
      }
 
      public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                           Expression<Func<T, bool>> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
      }
    }

    public static class utilities
    {
        private static Int32 _maximoDiasOperacionValida = 0;
        public static Int32 MaximoDiasOperacionValida
        {
            set { _maximoDiasOperacionValida = value; }
            get
            {

                return (_maximoDiasOperacionValida == 0 ?
                    Int32.Parse(UtilityDB.GetVal(string.Format("Select Valor from parametros where item = {0}", (byte)Pars.MaximoDiasOperacion))) :
                    _maximoDiasOperacionValida);
            }
        }

        public static void UpgradeDatabase()
        {
        }

        public static void CopyProperties(object objFrom, object objTo, string[] ExcludeProperties)
        {
            foreach (System.Reflection.PropertyInfo oProperty in objFrom.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if ((ExcludeProperties == null) || !ExcludeProperties.Contains(oProperty.Name))
                {
                    System.Reflection.PropertyInfo oNewProperty = objTo.GetType().GetProperty(oProperty.Name);

                    if (oNewProperty == null)  // try with uppercase
                    {
                        oNewProperty = objTo.GetType().GetProperty(oProperty.Name.ToUpper());
                    }

                    if (oNewProperty != null && oNewProperty.CanWrite)
                    {
                        oNewProperty.SetValue(objTo, oProperty.GetValue(objFrom, null), null);
                    }
                }
            }
        }

        #region GanagroLite framework

        public static void Guardar_View<T>(BindingListView<T> blv, ref string InfoMessage, ref string errorMessage, ref string ultimoCodigo, bool SuppressValidation) where T : IBO
        {
            int total = 0;
            InfoMessage = string.Empty;
            var sbErrors = new StringBuilder(string.Empty);
            try
            {
                foreach (IBO item in blv)
                {

                    if (!string.IsNullOrEmpty(item.CODIGO))
                        if (item.IsValid(SuppressValidation))
                        {
                            item.Guardar();
                            ultimoCodigo = item.CODIGO;
                            total++;
                        }
                        else
                        {
                            sbErrors.Append(string.Format("codigo {0} error: {1}\r\n", item.CODIGO, item.Error));
                            IOUtilities.LogitToDisk("LogCodigosIncorrectos.log", string.Format("codigo {0} error: {1}\r\n", item.CODIGO, item.Error));
                        }
                }

                errorMessage = (!string.IsNullOrEmpty(sbErrors.ToString()) ? "Datos invalidos: \r\n" + sbErrors.ToString() : string.Empty);

                InfoMessage = string.Format("Se han salvado {0} registros", total);


            }
            catch (Exception ex)
            {
                throw (ex);
            }
        }

        #endregion

        #region common validations on BOs
        public static bool NoDuplicates(string codigo, List<string> ls)
        {
            return ls.Count(item => EqualityComparer<string>.Default.Equals(item, codigo)) < 1;
        }

        public static bool CodigoEsValido(string codigo, ref string message, TiposOperacion op)
        {
            bool IsValid = true;
            message = string.Empty;
            using (GlContext glContext = new GlContext())
            {
                switch (op)
                {
                    case TiposOperacion.compra:
                        if (glContext.GANADOes.Any(p => p.CODIGO == codigo && p.Activo == 1))
                        {
                            message = "Codigo ya existe";
                            IsValid = false;
                        }
                        if (glContext.GANADOes.Any(p => p.CODIGO == codigo && p.Activo == 0))
                        {
                            IsValid = false;
                            message = string.Format("Codigo existe en historico");
                        }
                        break;
                    case TiposOperacion.venta:
                    case TiposOperacion.Muerte:
                    case TiposOperacion.Pesaje_Carne:
                        if (glContext.GANADOes.Any(g => g.CODIGO == codigo) == false)
                        {
                            message = "Codigo no existe";
                            IsValid = false;
                        }
                        else
                        {
                            List<string> retvals = UtilityDB.GetVals(string.Format("SELECT ACTIVO,TIPOSALIDA FROM GANADO WHERE CODIGO = '{0}'", codigo));
                            if ((byte.Parse(retvals[0]) == 0))
                            {
                                TiposOperacion tOp = (TiposOperacion)short.Parse(retvals[1]);
                                message = string.Format("Codigo de semoviente {0}", (tOp == TiposOperacion.venta) ? "vendido" : "muerto");
                                IsValid = false;
                            }
                        }
                        break;
                    case TiposOperacion.Otras:
                        if (glContext.GANADOes.Any(g => g.CODIGO == codigo) == false)
                        {
                            message = "Codigo no existe";
                            IsValid = false;
                        }
                        break;
                    default:
                        break;
                }
            }
            return IsValid;
        }

        public static bool EsFechaValida(string CODIGO, DateTime FECHA, short? OPERACION, Csla.Validation.RuleArgs e)
        {
            bool isvalid = true;

            if (string.IsNullOrEmpty(CODIGO))
            {
                return true;
            }

            if (FECHA < DateTime.Now.AddDays(utilities.MaximoDiasOperacionValida * -1))
            {
                e.Description = string.Format("La fecha no puede ser anterior a {0} dias", utilities.MaximoDiasOperacionValida);
                e.Severity = RuleSeverity.Error;
                return false;
            }

            if (FECHA > DateTime.Now)
            {
                e.Description = "La fecha no puede ser en el futuro";
                e.Severity = RuleSeverity.Error;
                return false;
            }

            switch ((TiposOperacion)OPERACION)
            {
                case TiposOperacion.Pesaje_Carne:
                    if (FechaPesajeYaExiste(FECHA, CODIGO))
                    {
                        e.Description = "Ya existe un pesaje en esta fecha.";
                        e.Severity = RuleSeverity.Error;
                        isvalid = false;
                    }
                    if (FechaUltimoPesajeEsPosterior(FECHA, CODIGO))
                    {
                        e.Description = "La fecha es anterior al ultimo pesaje de este semoviente";
                        e.Severity = RuleSeverity.Error;
                        isvalid = false;
                    }
                    break;
                case TiposOperacion.venta:
                case TiposOperacion.Muerte:
                    if (FechaUltimaOperacionEsPosterior(FECHA, CODIGO))
                    {
                        e.Description = "La fecha no puede ser anterior a la ultima operacion de este semoviente";
                        e.Severity = RuleSeverity.Error;
                        isvalid = false;
                    }
                    if (FechaUltimoPesajeEsPosterior(FECHA, CODIGO))
                    {
                        e.Description = "La fecha es anterior al ultimo pesaje de este semoviente";
                        e.Severity = RuleSeverity.Error;
                        isvalid = false;
                    }
                    break;
                default:
                    break;
            }


            return isvalid;
        }

        public static bool FechaValida<T>(T target, Csla.Validation.RuleArgs e) where T : IOperacion
        {
            return EsFechaValida(target.CODIGO, target.FECHA, target.OPERACION, e);
        }

        public static bool ValidaPeso(short? Peso, Csla.Validation.RuleArgs e)
        {
            e.Description = string.Empty;

            if (Peso == null)
            {
                e.Description = string.Format("Peso no puede ser nulo");
                return false;
            }

            if ((Peso > short.Parse(utilities.GetParametro(Pars.PesoMaximo))) || (Peso < short.Parse(utilities.GetParametro(Pars.PesoMinimo))))
            {
                e.Description = string.Format("Peso debe estar entre {0} y {1} Kilos", utilities.GetParametro(Pars.PesoMinimo), utilities.GetParametro(Pars.PesoMaximo));
                return false;
            }

            return true;

        }

        #endregion

        #region Generic Gets from DB

        public static Dictionary<Pars, string> parametrosGanagro;

        public static string GetParametro(Pars par)
        {
            if (parametrosGanagro == null)
            {
                using (GlContext glContext = new GlContext())
                {
                    parametrosGanagro = new Dictionary<Pars, string>();
                    foreach (var item in glContext.PARAMETROS)
                    {
                        parametrosGanagro.Add((Pars)item.ITEM, item.VALOR);
                    }
                }
            }

            return parametrosGanagro[par] ?? string.Empty;

        }

        public static bool FechaUltimaOperacionEsPosterior(DateTime fecha, string codigo)
        {
            bool success = false;
            if (UtilityDB.Exists("OPERACIONES", string.Format("CODIGO = '{0}' AND FECHA > '{1}'", codigo, fecha)))
            {
                success = true;
            }

            return success;
        }

        public static bool FechaUltimoPesajeEsPosterior(DateTime fecha, string codigo)
        {
            bool success = false;
            if (UtilityDB.Exists("PESAJES", string.Format("CODIGO = '{0}' AND FECHA > '{1}'", codigo, fecha)))
            {
                success = true;
            }

            return success;
        }

        public static bool FechaUltimaOperacionEsPosterior(DateTime fecha, string codigo, short? tOp)
        {
            bool success = false;

            if (UtilityDB.Exists("OPERACIONES", string.Format("CODIGO = '{0}' AND FECHA > '{1}' AND OPERACION = {2}", codigo, fecha, tOp)))
            {
                success = true;
            }

            return success;
        }

        public static bool FechaUltimaOperacionYaExiste(DateTime fecha, string codigo, short? tOp)
        {
            bool success = false;
            //            if (OPERACIONE.All().Any(t => (t.CODIGO == codigo) && (t.OPERACION == tOp) && (t.FECHA == fecha )))
            if (UtilityDB.Exists("OPERACIONES", string.Format("CODIGO = '{0}' AND FECHA = '{1}' AND OPERACION = {2}", codigo, fecha, tOp)))
            {
                success = true;
            }

            return success;
        }

        public static bool FechaPesajeYaExiste(DateTime fecha, string codigo)
        {

            bool success = false;

            //            if (PESAJE.All().Any(t => (t.Codigo == codigo) && (t.Fecha== fecha)))
            if (UtilityDB.Exists("PESAJES", string.Format("CODIGO = '{0}' AND FECHA = '{1}' ", codigo, fecha)))
            {
                success = true;
            }

            return success;
        }

        public static bool FechaAnteriorAEntrada(DateTime fecha, string codigo)
        {
            bool success = false;
            //            if (GANADO.All().Any(t => t.CODIGO == codigo && t.FECHA_ENTRADA > fecha))
            if (UtilityDB.Exists("GANADO", string.Format("CODIGO = '{0}' AND FECHA_ENTRADA > '{1}' ", codigo, fecha)))
            {
                success = true;
            }
            return success;
        }

        public static string GetParametro()
        {
            return string.Empty;
        }

        public static string GetDescOperacion(TiposOperacion tOp)
        {
            string descripcion = string.Empty;
            switch (tOp)
            {
                case TiposOperacion.Pesaje_Carne:
                    descripcion = "Pesaje de Carne";
                    break;
                case TiposOperacion.compra:
                    descripcion = "Compra";
                    break;
                case TiposOperacion.venta:
                    descripcion = "Venta";
                    break;
                case TiposOperacion.Muerte:
                    descripcion = "Muerte";
                    break;
                default:
                    break;
            }

            return descripcion;
        }

        public static void CambiarGanadodeLoteYRegistrarOperacion(BindingList<GANADO_BO> Semovientes, byte loteId, DateTime DT)
        {

            List<string> nuevoscodigos = (from p in Semovientes where p.Isvalid() select p.CODIGO).ToList<string>();
            using (GlContext glContext = new GlContext())
            {
                foreach (string codigo in nuevoscodigos)
                {
                    CambiarLote(codigo, loteId);
                    // Guardar registro en operaciones
                    OPERACIONE op = new OPERACIONE() { CODIGO = codigo, OPERACION = 20, FECHA = DT, VALOR1 = loteId };
                    glContext.OPERACIONES.Add(op);
                }

                glContext.SaveChanges();
            }
        }

        public static void CambiarLote(string codigo, int lote)
        {
            UtilityDB.ExecuteCommand(string.Format("UPDATE GANADO SET LOTE = {0} WHERE CODIGO = '{1}'", lote, codigo));
        }

        #endregion

    }
}
